package Generic;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Population implements Iterable<Individual>{
	private List<Individual> individuals;
	private int generation;
	
	private Population()
	{
		individuals = new ArrayList<Individual>();
		generation = 0;
	}
	
	public static Population getFirstPopulation()
	{
		return new Population();
	}
	
	public Population createChildPopulation()
	{
		Population ret = new Population();
		ret.generation = generation+1;
		return ret;
	}

	public double getMaxFitness() {
		return java.util.Collections.max(individuals).getFitness();
	}

	public double getAverageFitness() {
		return reduce(new Sum(),new Identity(),0.0)/individuals.size();
	}
	
	private static interface Transformer{public double transform(double value);}
	private static class Identity implements Transformer{public double transform(double value){return value;}}
	private static class Square implements Transformer{public double transform(double value){return value*value;}}
	
	private static interface Reducer{public double reduce(double value1,double value2);}
	private static class Sum implements Reducer{public double reduce(double value1,double value2){return value1+value2;}};
	
	private double reduce(Reducer reducer,Transformer transformer,double start)
	{
		double aggregate = start;
		for(Individual ind : individuals)
		{
			aggregate = reducer.reduce(aggregate, transformer.transform(ind.getFitness()));
		}
		return aggregate;
	}

	public void addIndividual(Individual individual) {
		individuals.add(individual);
	}

	public void incrementAge() {
		for(Individual ind : individuals)
		{
			ind.incrementAge();
		}
	}

	public int getSize() {
		return individuals.size();
	}

	public Iterator<Individual> iterator() {
		return individuals.iterator();
	}

	public void removeIndividual(Individual ind) {
		individuals.remove(ind);
	}

	public void shuffle() {
		java.util.Collections.shuffle(individuals);
	}

	public void addPopulation(Population population) {
		individuals.addAll(population.individuals);
	}

	public void keepBestIndividuals(int populationSize) {
		java.util.Collections.sort(individuals);
		while(individuals.size()>populationSize)
		{
			individuals.remove(0);
		}
	}

	public Individual getIndividual(int index) {
		return individuals.get(index);
	}

	public double getStandardDeviation() {
		double sum = reduce(new Sum(),new Identity(),0);
		double squareSum = reduce(new Sum(),new Square(),0);
		double size = individuals.size();
		
		return Math.sqrt(squareSum/size-sum*sum/size/size);
	}

	public Individual getBestIndividual() {
		return java.util.Collections.max(individuals);
	}

	public int getGeneration() {
		return generation;
	}
}
